package com.paradogs.framework.annotations.exceptionHandler;

import com.paradogs.framework.annotations.ParadogsAnnotationsHolder;
import com.paradogs.framework.server.ParadogsContext;
import com.paradogs.framework.utils.MsgUtils;
import com.paradogs.common.utils.SpringUtils;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.stream.Stream;

/**
 * @author: yumi
 * @date: 2023/8/2  9:12
 * @Description: 全局异常处理管理器
 */
@Slf4j
@Component
public class ParadogsExceptionHandlerManager {

    /** 注册异常处理方法映射 */
    @PostConstruct
    public void registerExceptionHandler() {
        for (Object bean : SpringUtils.getBeansWithAnnotation(PRExceptionHandler.class).values()) {
            for (Method method : bean.getClass().getDeclaredMethods()) {
                PRExceptionMapping PRExceptionMapping = method.getAnnotation(PRExceptionMapping.class);
                if (PRExceptionMapping == null) { continue; }
                // 注册异常处理器路由，可覆盖 paradogs-framework
                ParadogsAnnotationsHolder.ExceptionHandlerMap.put(PRExceptionMapping.value(), method);
            }
        }
    }

    /** 将异常转发到对应异常处理方法: 会自动优先找最相近的类 */
    public static void forward(Throwable throwable, ParadogsContext ctx) {

        if (throwable == null) {return;}

        Throwable realThrowable = throwable.getCause() != null ? throwable.getCause() : throwable;

        // 这里的异常时 InvocationTargetException 反射异常，转换成真正的异常
        Method method = ParadogsAnnotationsHolder.ExceptionHandlerMap.get(realThrowable.getClass());

        // 无任何处理器包括 ExceptionHandler 时，直接结束
        if (method == null && (method = ParadogsAnnotationsHolder.ExceptionHandlerMap.get(Throwable.class)) == null) {
            log.error("{}", realThrowable);
            return;
        }

        try {
            // 处理方法
            Object result = method.invoke(
                    SpringUtils.getBean(method.getDeclaringClass()),
                    Stream.of(method.getParameterTypes())
                            .map(type -> Throwable.class.isAssignableFrom(type) ? realThrowable : null)
                            .toArray()
            );
            if (result == null || ctx.getExec() != null) { return; }

            // 将返回值作为 gc 消息返回，ws 要额外包一层 BinaryWebSocketFrame
            MsgUtils.notifyClient(ctx.getChannel(), MsgUtils.ROUTE_ERR, result, null, ctx.getMessage().getHeader().getId());

        } catch (Exception e) {
            // 异常处理时出现的异常
            log.error("{}", e);
        }
    }

}
